package com.fuzongle.tenant.core.mybatis.parser;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.fuzongle.tenant.core.cache.EntityTableCache;
import com.fuzongle.tenant.core.mybatis.TenantProperties;
import com.fuzongle.tenant.core.mybatis.handler.TenantGlobalColumnHandler;
import com.fuzongle.tenant.utils.EntityUtils;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.util.deparser.ExpressionDeParser;
import net.sf.jsqlparser.util.deparser.SelectDeParser;
import net.sf.jsqlparser.util.deparser.UpdateDeParser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author: fuzongle
 * @description:  SQL 解析器
 **/
@Component
public class SqlParser {

    private static TenantProperties tenantProperties;

    @Autowired
    public SqlParser(TenantProperties tenantProperties) {
        SqlParser.tenantProperties = tenantProperties;
    }


    protected static class SQLParserSelect extends SelectDeParser {
        private String schemaName;

        public SQLParserSelect(ExpressionVisitor expressionVisitor, StringBuilder buffer) {
            super(expressionVisitor, buffer);
        }

        public String getSchemaName() {
            return schemaName;
        }

        public void setSchemaName(String schemaName) {
            this.schemaName = schemaName;
        }

        @Override
        public void visit(Table tableName) {
            tableName.setSchemaName(schemaName);
            StringBuilder buffer = getBuffer();
            buffer.append(tableName.getFullyQualifiedName());
            Pivot pivot = tableName.getPivot();
            if (pivot != null) {
                pivot.accept(this);
            }
            Alias alias = tableName.getAlias();
            if (alias != null) {
                buffer.append(alias);
            }
        }
    }



    public static String doInsert(Insert insert, String schemaName) {
        String tableName = insert.getTable().getName();
        //校验系统非追加表
        if (tenantProperties.getIgnoreTables().contains(tableName)){
            return insert.toString();
        }
        //获取表对应实体路劲
        String entityPath = EntityTableCache.getInstance().getCacheData(tableName).toString();
        //判断实体是否有createdBy属性,追加create_by
        if (EntityUtils.isHaveAttr(entityPath, TenantGlobalColumnHandler.COLUMN_CREATED_BY_ENTITY)) {
            handleColumnsAndExpressions(insert, TenantGlobalColumnHandler.COLUMN_CREATED_BY);
        }
        //判断实体是否有updateBy属性,追加update_by
        if (EntityUtils.isHaveAttr(entityPath,TenantGlobalColumnHandler.COLUMN_UPDATED_BY_ENTITY)) {
            handleColumnsAndExpressions(insert,TenantGlobalColumnHandler.COLUMN_UPDATED_BY);
        }
        //追加tenant_id
        insert.getColumns().add(new Column(TenantGlobalColumnHandler.COLUMN_TENANT_ID));
        ((ExpressionList) insert.getItemsList()).getExpressions().add(TenantGlobalColumnHandler.getTenantId());
        //是否设置库名
        Table table = insert.getTable();
        table.setSchemaName(schemaName);
        insert.setTable(table);
        return insert.toString();
    }


    public static String doDelete(Delete delete, String schemaName) {
        String tableName = delete.getTable().getName();
        //校验系统非追加表
        if (tenantProperties.getIgnoreTables().contains(tableName)){
            return delete.toString();
        }
        //构建where条件
        BinaryExpression binaryExpression = andExpression(delete.getTable(), delete.getWhere());
        //追加where条件
        delete.setWhere(binaryExpression);
        //设置库名
        Table t = delete.getTable();
        t.setSchemaName(schemaName);
        delete.setTable(t);
        return delete.toString();
    }


    public static String doUpdate(Update update, String schemaName) throws JSQLParserException{
        String tableName = update.getTable().getName();
        //校验系统非追加表
        if (tenantProperties.getIgnoreTables().contains(tableName)){
            return update.toString();
        }
        //构建where条件
        BinaryExpression binaryExpression = andExpression(update.getTable(), update.getWhere());
        //追加where条件
        update.setWhere(binaryExpression);
        //获取表对应实体路劲
        String entityPath = EntityTableCache.getInstance().getCacheData(tableName).toString();
        //判断实体是否有updateBy属性,追加update_by
        if (EntityUtils.isHaveAttr(entityPath,TenantGlobalColumnHandler.COLUMN_UPDATED_BY_ENTITY)) {
            handleColumnsAndExpressions(update,TenantGlobalColumnHandler.COLUMN_UPDATED_BY);
        }

        //追加库名
        StringBuilder buffer = new StringBuilder();
        Table tb = update.getTable();
        tb.setSchemaName(schemaName);
        update.setTable(tb);
        // 处理from
        FromItem fromItem = update.getFromItem();
        if (fromItem != null) {
            Table tf = (Table) fromItem;
            tf.setSchemaName(schemaName);
        }
        // 处理join
        List<Join> joins = update.getJoins();
        if (joins != null && joins.size() > 0) {
            for (Object object : joins) {
                Join t = (Join) object;
                Table rightItem = (Table) t.getRightItem();
                rightItem.setSchemaName(schemaName);
                System.out.println();
            }
        }
        ExpressionDeParser expressionDeParser = new ExpressionDeParser();
        UpdateDeParser p = new UpdateDeParser(expressionDeParser, null, buffer);
        expressionDeParser.setBuffer(buffer);
        p.deParse(update);

        return update.toString();
    }



    public static String doSelect(Select select, String schemaName){
        processPlainSelect((PlainSelect) select.getSelectBody());
        StringBuilder buffer = new StringBuilder();
        ExpressionDeParser expressionDeParser = new ExpressionDeParser();
        SQLParserSelect parser = new SQLParserSelect(expressionDeParser, buffer);
        parser.setSchemaName(schemaName);
        expressionDeParser.setSelectVisitor(parser);
        expressionDeParser.setBuffer(buffer);
        select.getSelectBody().accept(parser);

        return buffer.toString();
    }

    /**
     * 处理 PlainSelect
     */
    private static void processPlainSelect(PlainSelect plainSelectStatement) {
        processPlainSelect(plainSelectStatement, false);
    }

    /**
     * 处理 PlainSelect
     *
     * @param plainSelectStatement ignore
     * @param addColumn            是否添加租户列,insert into select语句中需要
     */
    private static void processPlainSelect(PlainSelect plainSelectStatement, boolean addColumn) {
        FromItem fromItem = plainSelectStatement.getFromItem();
        if (fromItem instanceof Table) {
            Table fromTable = (Table) fromItem;
            if (tenantProperties.getIgnoreTables().contains(fromTable.getName())){
                return;
            }
            BinaryExpression binaryExpression = andExpression(fromTable, plainSelectStatement.getWhere());
            plainSelectStatement.setWhere(binaryExpression);
            if (addColumn) {
                List<SelectItem> columnList = plainSelectStatement.getSelectItems();
                columnList.add(new SelectExpressionItem(new Column(TenantGlobalColumnHandler.COLUMN_TENANT_ID)));
                columnList.add(new SelectExpressionItem(new Column(TenantGlobalColumnHandler.COLUMN_IS_DELETE)));
            }
        }

    }

    /**
     * delete update 语句 where 处理
     */
    protected static BinaryExpression andExpression(Table table, Expression where) {
        //获得where条件表达式
        EqualsTo equalsTo = new EqualsTo();
        equalsTo.setLeftExpression(getIsDeleteAliasColumn(table));
        equalsTo.setRightExpression(TenantGlobalColumnHandler.getIsDeleteValue());

        EqualsTo equalsTo2 = new EqualsTo();
        equalsTo2.setLeftExpression(getTenantAliasColumn(table));
        equalsTo2.setRightExpression(TenantGlobalColumnHandler.getTenantId());

        if (null != where) {
            if (where instanceof OrExpression) {
                return new AndExpression(new AndExpression(equalsTo, equalsTo2), new Parenthesis(where));
            } else {
                return new AndExpression(new AndExpression(equalsTo, equalsTo2), where);
            }
        }
        return new AndExpression(equalsTo, equalsTo2);
    }

    /**
     * 租户id字段
     */
    private static Column getTenantAliasColumn(Table table) {
        StringBuilder column = new StringBuilder();
        if (null == table.getAlias()) {
            column.append(table.getName());
        } else {
            column.append(table.getAlias().getName());
        }
        column.append(StringPool.DOT);
        column.append("tenant_id");
        return new Column(column.toString());
    }


    /**
     * isDelete字段
     */
    private static Column getIsDeleteAliasColumn(Table table) {
        StringBuilder column = new StringBuilder();
        if (null == table.getAlias()) {
            column.append(table.getName());
        } else {
            column.append(table.getAlias().getName());
        }
        column.append(StringPool.DOT);
        column.append("is_delete");
        return new Column(column.toString());
    }
    /**
     * @description: 新增更新操作字段
     * @param obj
     * @param columnName
     * @return: void
     **/
    private static void handleColumnsAndExpressions(Object obj, String columnName) {
        //操作列字段
        List<Column> columns = new ArrayList<>();
        //操作值
        List<Expression> expressions = new ArrayList<>();
        String aliaName = "";
        //新增操作
        if (obj instanceof Insert) {
            Insert insertStatement = (Insert) obj;
            expressions = ((ExpressionList) insertStatement.getItemsList()).getExpressions();
            columns = insertStatement.getColumns();
        }
        //修改操作
        if (obj instanceof Update) {
            Update updateStatement = (Update) obj;
            Table table = updateStatement.getTable();
            Alias alias = Optional.ofNullable(table.getAlias()).orElse(new Alias(table.getName()));
            columns = updateStatement.getColumns();
            expressions = updateStatement.getExpressions();
            aliaName = alias.getName() + StringPool.DOT;
        }
        //是否包含列
        long count = columns.stream().filter(
                column -> column.getColumnName().equals(columnName) || column.getColumnName().equals(StringPool.BACKTICK + columnName + StringPool.BACKTICK)
        ).count();

        if (count == 0) {
            columns.add(new Column(aliaName + columnName));
            expressions.add(new StringValue("测试更新人数据值"));
        }
    }




}
